/**
 * Mapper代理工厂类
 * 用于创建Mapper接口的代理实例
 *
 * @author jms
 * @since 2025-01-06
 *
 * @param <T> Mapper接口的类型
 */
package com.jms.mybatis.plus.factory;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MapperProxyFactory {
    /**
     * 创建Mapper接口的代理实例
     *
     * @return 返回Mapper接口的代理实例
     */
    @SuppressWarnings("unchecked")
    public static <T> T getProxy(Class<T> mapperInterface, SqlSessionFactory sqlSessionFactory, SqlSession sqlSession) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface },
                new MapperInvocationHandler(sqlSessionFactory, sqlSession, mapperInterface));
    }

    private static class MapperInvocationHandler implements InvocationHandler {

        /**
         * SqlSession, 用于获取Mapper接口代理的实例
         */
        private final SqlSession sqlSession;

        private final SqlSessionFactory sqlSessionFactory;

        /**
         * mapperInterface表示Mapper接口的Class对象
         * 用于获取Mapper接口的相关信息
         */
        private final Class<?> mapperInterface;

        /**
         * 构造函数，初始化MapperInvocationHandler实例
         *
         * @param sqlSession SqlSession实例，用于获取Mapper接口代理的实例
         * @param mapperInterface Mapper接口的Class对象，用于获取Mapper接口信息
         */
        public MapperInvocationHandler(SqlSessionFactory sqlSessionFactory, SqlSession sqlSession, Class<?> mapperInterface) {
            this.sqlSessionFactory = sqlSessionFactory;
            this.sqlSession = sqlSession;
            this.mapperInterface = mapperInterface;
        }

        /**
         * 拦截并处理对Mapper接口方法的调用
         * 如果被调用的方法属于Object类，则直接调用
         * 否则, 获取对应的Mapper实例，并委托其执行方法
         *
         * @param proxy 代理对象，这里通常不用
         * @param method 被调用的方法的Method对象
         * @param args 方法的参数
         * @return 方法的返回值
         * @throws Throwable 方法执行过程中可能抛出的异常
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 检查被调用的方法是否属于Object类
            if (Object.class.equals(method.getDeclaringClass())) {
                // 如果是Object类的方法，直接调用
                return method.invoke(this, args);
            } else {
                if (sqlSession != null) {
                    // 委托Mapper实例执行方法，并返回结果
                    return method.invoke(sqlSession.getMapper(mapperInterface), args);
                } else {
                    try (SqlSession newSqlSession = sqlSessionFactory.openSession(true)) {
                        return method.invoke(newSqlSession.getMapper(mapperInterface), args);
                    }
                }
            }
        }
    }
}
